package com.ht.api.task.ad;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import com.ht.api.business.ad.PlanHelper;
import com.ht.api.db.Db;
import com.ht.api.db.DbConfig;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.StreamLoadConfig;
import com.ht.api.db.task.HtTaskExecutor;
import com.ht.api.db.task.TaskGroup;
import com.ht.api.db.task.TaskHandler;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ht.api.db.util.HtUtil;
import com.ht.api.util.NoticeUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 创建计划用户任务
 */
@Slf4j
public class CreatePlanUserTask implements TaskHandler {

	@Override
	public String getName() {
		return "创建计划用户任务	";
	}

    @Override
    public boolean handler(JSONObject requestParam, String groupId, long taskId) {
        // 获取数据源
        String userId = requestParam.getStr("user_id");
        long tenantId = requestParam.getLong("tenant_id");
        DataSource ds = DbConfig.useTenantDb(tenantId);
        StreamLoadConfig config = DbConfig.useTenantSLC(tenantId);

        Entity plan = requestParam.getBean("plan", Entity.class);
        String campaignId = plan.getStr("campaign_id");

        List<Entity> dataList = new ArrayList<>();

        JSONArray audienceRule = plan.getJSONArray("audience_rule");
        JSONObject conditionRule = plan.getJSONObject("condition");
        JSONObject appRule = plan.getJSONObject("app_rule");

        // 查找符合条件的oneids
        String oneIds = PlanHelper.findOneIdsByAppAndRule(ds, appRule, audienceRule, conditionRule);

        // 根据找到的用户，生成广告计划与用户的关联数据
        JSONObject paramPlanUser = JSONUtil.createObj();
        paramPlanUser.set("source_id", plan.get("source_id"));
        paramPlanUser.set("source_type", plan.get("source_type"));
        paramPlanUser.set("plan_id", plan.getMainId());
        List<Entity> planUserList = PlanHelper.createPlanUser(oneIds, paramPlanUser);

        dataList.addAll(planUserList);

        Db.insertBatchWithStream(config, dataList);

        // 查找满足策略和计划的触点用户
        int groupCount = 0; //如果需要检查创意时，在创建任务组时有用
        DbList campaignItems = Db.createSqlParser("ad_campaign_item").where("campaign_id", campaignId).query(ds);
        Map<String, DbList> usersMap = new HashMap<>(); // 触点用户map，避免重复查询
        for (Entity campaignItem : campaignItems) {
            String adPosition = campaignItem.getStr("ad_position");
            // 根据app范围和广告位，获取对应的触点关系
            if (!usersMap.containsKey(adPosition)) {
                DbList userList = PlanHelper.findTouchUserList(tenantId, plan.getMainId(), appRule, adPosition);
                usersMap.put(adPosition, userList);
            }

            groupCount += usersMap.get(adPosition).size();
        }

        // 创建广告任务
        createPlanTask (ds, config, tenantId, plan, campaignItems, usersMap, groupCount, userId);

        return true;
    }


    /**
     * 创建计划广告任务
     * @param ds
     * @param tenantId
     * @param plan
     * @param campaignItems
     * @param usersMap
     * @param groupCount
     */
    public void createPlanTask (DataSource ds, StreamLoadConfig config, long tenantId, Entity plan, DbList campaignItems, Map<String, DbList> usersMap, int groupCount, String userId) {
        // 任务列表，后续会用来组装任务组
        List<Entity> taskList = new ArrayList<>();

        int checkCreative = plan.getInt("check_creative");

        TaskGroup group = null;
        TaskHandler task = null;
        if (checkCreative == 1) {//若需要检查创意的重复性，那么得先创建一个任务组，然后让每一个用户对每一个策略单独进行检测执行
            // 创建一个任务组，循环执行触点用户，并生成对应的广告
            JSONObject paramGroup = JSONUtil.createObj();
            paramGroup.set("tenant_id", tenantId);
            paramGroup.set("plan_id", plan.getMainId());
            paramGroup.set("plan_name", plan.getStr("name"));
            paramGroup.set("user_id", userId);
            group = TaskGroup.create(paramGroup, groupCount, new CreatePlanTaskCallback());
            task = new CreatePlanTask(); // 任务执行类

            // 这是任务组ID，可根据此ID调接口来查询任务组进度情况
            plan.set("group_task_id", group.getId());
            Db.insert(ds, plan);
        }

        // 循环策略，并根据用户生成相应的广告任务
        for (Entity campaignItem : campaignItems) {
            String adPosition = campaignItem.getStr("ad_position");

            DbList userList = usersMap.get(adPosition);

            if (checkCreative == 2) { //不检查创意

                notCheckCreative (ds, plan, campaignItem, userList, taskList);

            } else {//检查创意
                // 触点用户生成广告任务
                for (Entity touchUser : userList) {
                    JSONObject paramObj = JSONUtil.createObj();
                    paramObj.set("tenant_id", tenantId);
                    paramObj.set("plan", plan);
                    paramObj.set("touchUser", touchUser);
                    paramObj.set("campaignItem", campaignItem);

                    HtTaskExecutor.submitTask(group, paramObj, task);
                }

            }

        }

        if (taskList.size() > 0) {
            Db.insertBatchWithStream(config, taskList);
            PlanHelper.createAdTaskGroup(tenantId, taskList, plan.getMainId());
            plan.set("status", 2);
            Db.insert(ds, plan);

            if (StrUtil.isNotBlank(userId)) {
                NoticeUtil.send( "广告计划["+plan.getStr("name")+"]已启动成功！", 1, userId);
            }

            HtUtil.createLog(ds, plan.getMainId(), "初始化完成，开始执行中。");
        }



    }


    /**
     * 无需检测创意重复性的广告任务创建逻辑
     * @param ds
     * @param plan
     * @param campaignItem
     * @param userList
     * @param taskList
     */
    public void notCheckCreative (DataSource ds, Entity plan, Entity campaignItem, DbList userList, List<Entity> taskList) {
        String creativeId = campaignItem.getStr("creative_id");

        // 当广告为朋友圈时，判断是否是全量用户推送，如果是，则需要在任务上加上朋友圈标签ID，避免打标签耗时
        String adPosition = campaignItem.getStr("ad_position");
        boolean pushAllToMomentStatus = false;
        if (adPosition.equals("moment") || adPosition.equals("private_chat")) {
            JSONArray audienceRule = plan.getJSONArray("audience_rule");
            JSONObject conditionRule = plan.getJSONObject("condition");
            if (audienceRule == null && conditionRule == null) {
                pushAllToMomentStatus = true;
            }
        }

        // 确定此条策略的具体创意
        Entity creative = Entity.create();
        if (StrUtil.isNotBlank(creativeId)) {
            creative = Db.createSqlParser("creative").where("main_id", creativeId).findOne(ds);
        } else {
            String adId = campaignItem.getStr("ad_id");
            creative = Db.createSqlParser("creative").where("ad_id", adId).findOne(ds);
        }

        int adType = Db.createSqlParser("ad").select("ad_type").where("main_id", creative.get("ad_id")).val(ds);
        creative.set("ad_type", adType);

        // 组装广告任务
        for (Entity touchUser : userList) {
            Entity userAdTask = PlanHelper.buildAdTaskToUser(plan, touchUser, campaignItem, creative);

            if (pushAllToMomentStatus) {
                userAdTask.set("moment_partition", touchUser.getStr("moment_partition"));
            }

            taskList.add(userAdTask);
        }

    }
}
